package com.lwc.backendsinglestructure.aspect;


import cn.hutool.json.JSONUtil;
import com.lwc.backendsinglestructure.annotation.DistributeLock;
import com.lwc.backendsinglestructure.annotation.OperationLogInfo;
import com.lwc.backendsinglestructure.domain.entity.OperationLog;
import com.lwc.backendsinglestructure.exception.BusinessException;
import com.lwc.backendsinglestructure.exception.ErrorCode;
import com.lwc.backendsinglestructure.mapper.OperationLogMapper;
import com.lwc.backendsinglestructure.utils.ThreadLocalUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 操作日志的切面
 * @author Rel
 * @date 2025/8/28 17:01
 */
@Component
@Aspect
public class DistributeLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 以自定义 @OperationLog 注解为切点
     */
    @Pointcut("@annotation(com.lwc.backendsinglestructure.annotation.DistributeLock)")
    public void distributeLock() {
    }

    /**
     * 拦截操作日志
     */
    @Around("distributeLock()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 1.获取被拦截的方法上的注解
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
        // 2.解析注解上的变量
        String key = distributeLock.key();
        if (key.equals("none")){
            // 使用spel解析key
            if ("none".equals(distributeLock.keyExpression())) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取锁失败");
            }
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(distributeLock.keyExpression());

            EvaluationContext context = new StandardEvaluationContext();
            // 获取参数值
            Object[] args = proceedingJoinPoint.getArgs();

            // 获取运行时参数的名称
            StandardReflectionParameterNameDiscoverer discoverer
                    = new StandardReflectionParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames(method);

            // 将参数绑定到context中
            if (parameterNames != null) {
                for (int i = 0; i < parameterNames.length; i++) {
                    context.setVariable(parameterNames[i], args[i]);
                }
            }

            // 解析表达式，获取结果
            key = String.valueOf(expression.getValue(context));
        }
        String lockKey = distributeLock.business() + ":" + key;
        // 获取过期时间
        long expireTime = distributeLock.expireTime();
        // 获取等待时间
        long waitTime = distributeLock.waitTime();
        // 3.获取一把锁锁
        RLock lock = redissonClient.getLock(lockKey);
        Object response;
        try {
            // 获取锁是否成功
            boolean lockResult = false;
            // 是否开启看门狗机制
            if (waitTime == -1L){
                if (expireTime == -1L){
                    lock.lock();
                }else {
                    lock.lock(expireTime, TimeUnit.SECONDS);
                }
                lockResult = true;
            }else {
                if (expireTime == -1L){
                    lock.tryLock(waitTime, TimeUnit.SECONDS);
                }else {
                    lock.tryLock(waitTime, expireTime, TimeUnit.SECONDS);
                }
                lockResult = true;
            }

            // 判断锁是否获取成功
            if (!lockResult){
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取锁失败");
            }
            // 4.调用目标方法
            response = proceedingJoinPoint.proceed();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 5.释放锁
            if (lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }

        return response;
    }



}
